﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections.Generic;

public class TcpClientEx
{
    /// <summary>
    /// 包大小（可包含信息 pakSize - 2)
    /// </summary>
    const ushort pakSize = 4096;

    TcpClient client;                     // 连接 socket

    NetworkStream ns;

    /// <summary>
    /// 断开连接事件
    /// </summary>
    public event Action OnDisconnect;
    /// <summary>
    /// 收到消息事件
    /// </summary>
    public event Action<byte[]> OnMsgReceived;


    public TcpClientEx()
    {
        client = new TcpClient();

        client.NoDelay = true;
        client.SendTimeout = 10000;
        client.ReceiveTimeout = 10000; 
    }
    public TcpClientEx(TcpClient client)
    {
        this.client = client;
        client.NoDelay = true;
        client.SendTimeout = 10000;
        client.ReceiveTimeout = 10000;

        ns = this.client.GetStream();
        Thread td = new Thread(receiveProc);
        td.IsBackground = true;
        td.Start();
    }

    /// <summary>
    /// 设置超时时间（发送和接收）
    /// </summary>
    /// <param name="milisecond"></param>
    public void SetTimeout(int milisecond)
    {
        SetSendTimeout(milisecond);
        SetReceiveTimeout(milisecond);
    }

    /// <summary>
    /// 设置发送超时时间（默认 10 秒）
    /// </summary>
    /// <param name="milisecont"></param>
    public void SetSendTimeout(int milisecont)
    {
        client.SendTimeout = milisecont;
    }

    /// <summary>
    /// 设置接收超时时间（默认 10 秒）
    /// </summary>
    /// <param name="milisecond"></param>
    public void SetReceiveTimeout(int milisecond)
    {
        client.ReceiveTimeout = milisecond;
    }

    /// <summary>
    /// 连接远端服务器，成功返回 true，失败返回 false
    /// </summary>
    /// <param name="addr">远端地址</param>
    /// <param name="port">端口</param>
    /// <returns></returns>
    public bool Connect(IPAddress addr, int port)
    {
        try
        {
            if (Connected)
            {
                throw new Exception("这是一个活跃的连接");
            }
            client.Connect(addr, port);
            ns = client.GetStream();
            Thread td = new Thread(receiveProc);
            td.IsBackground = true;
            td.Start();
            return true;
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// 连接远端服务器，成功返回 true ，失败返回 false
    /// </summary>
    /// <param name="ip"></param>
    /// <param name="port"></param>
    /// <returns></returns>
    public bool Connect(string ip,int port)
    {
        IPAddress addr = IPAddress.Parse(ip);
        return Connect(addr, port);
    }

    /// <summary>
    /// 关闭连接
    /// </summary>
    public void Disconnect()
    {
        if (client != null)
        {
            client.Close();
            client = null;
        }
    }

    /// <summary>
    /// 是否已连接远端主机
    /// </summary>
    public bool Connected
    {
        get
        {
            if (client == null || client.Client == null) return false;
            return client.Client.Connected;
        }
    }



    /// <summary>
    /// 发送消息 成功返回true，失败返回 false
    /// </summary>
    /// <param name="msg">要发送的消息</param>
    /// <returns></returns>
    public bool Send(byte[] msg)
    {
        if (msg.Length > pakSize)
        {
            throw new Exception("消息包大小不能超过" + pakSize);
        }
        if (!Connected) return false;
        try
        {
            byte[] head = BitConverter.GetBytes((ushort)msg.Length);

            ns.Write(head, 0, head.Length);
            ns.Write(msg, 0, msg.Length);
            return true;
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// 异步 发送消息 
    /// </summary>
    /// <param name="msg">要发送的消息</param>
    public void SendAsync(byte[] msg)
    {
        Func<byte[],bool> func = Send;
        func.BeginInvoke(msg, null, null);
    }

    /// <summary>
    /// 接收消息的线程函数
    /// </summary>
    void receiveProc()
    {

        int receivedSize = 0;       // 接收到消息的长度

        byte[] msgSize = new byte[2]; // 支持的消息大小最大为 64K 
        bool readedHead = false;      // 标志是否已经读取了消息头
        byte[] msg = null;            // 消息实体

        while (true)
        {
            try
            {
                if (!readedHead) // 读取消息长度
                {
                    receivedSize += ns.Read(msgSize, receivedSize, 2 - receivedSize);

                    if (receivedSize < 2)
                        continue;

                    ushort length = BitConverter.ToUInt16(msgSize, 0); // 获取消息的长度

                    msg = new byte[length];
                    readedHead = true;
                    receivedSize = 0;
                }
                else // 读取消息实体
                {
                    receivedSize += ns.Read(msg, receivedSize, msg.Length - receivedSize);

                    if (receivedSize < msg.Length)
                        continue;
                    if (OnMsgReceived != null) // 触发收到消息的事件
                        OnMsgReceived.Invoke(msg);

                    readedHead = false;
                    receivedSize = 0;
                }
            }
            catch  // 对方异常终止了连接
            {
                break;
            }

        }
        if (ns != null)
            ns.Close();
        client.Close();
        if (OnDisconnect != null) // 触发断开连接的事件
            OnDisconnect.Invoke();
    }
}
